/**
 * 蛇
 */
export class Snake {

    /**
     * 蛇容器
     * @private
     */
    private _snake: HTMLElement;

    /**
     * 蛇头的位置
     * @private
     */
    private readonly _head: HTMLElement;

    /**
     * 蛇整个身体，包括蛇头
     * @private
     */
    private readonly _bodies: HTMLCollectionOf<HTMLElement>;

    /**
     * 是否存活
     * @private
     */
    private _isLive: boolean = true;

    /**
     * 当前前进方向
     * @private
     */
    private _currentDirection: string = "";

    /**
     * 需要移动的索引
     * @private
     */
    private _moveIndex: number | undefined;

    /**
     * 上
     * @private
     */
    private readonly _up: string[] = ["ArrowUp", "Up", "w", "W"];

    /**
     * 下
     * @private
     */
    private readonly _down: string[] = ["ArrowDown", "Down", "s", "S"];

    /**
     * 左
     * @private
     */
    private readonly _left: string[] = ["ArrowLeft", "Left", "a", "A"];

    /**
     * 右
     * @private
     */
    private readonly _right: string[] = ["ArrowRight", "Right", "d", "D"];

    constructor() {
        this._snake = document.getElementById("snake")!;
        this._head = document.querySelector<HTMLElement>("#snake > div")!;
        this._bodies = this._snake.getElementsByTagName("div");
    }

    /**
     * 是否存活
     */
    get isLive(): boolean {
        return this._isLive;
    }

    /**
     * 蛇头x坐标
     */
    get x(): number {
        return this._head.offsetLeft;
    }

    /**
     * 蛇头y坐标
     */
    get y(): number {
        return this._head.offsetTop;
    }

    /**
     * 蛇长度
     */
    get length(): number {
        return this._bodies.length;
    }

    /**
     * 当前前进方向
     */
    get currentDirection(): string {
        return this._currentDirection;
    }

    /**
     * 移动
     * @param direction     移动方向
     * @param step          移动步长
     * @param fn            坐标计算完成时回调函数
     */
    public move(direction: string, step: number, fn?: (x: number, y: number) => void): void {
        let x = this.x;
        let y = this.y;
        let length = this.length;
        switch (direction) {
            case "ArrowUp":
            case "Up":
            case "w":
            case "W":
                // 身体大于1 && 向反方向移动，修正为当前方向继续移动
                if (length > 1 && this._down.includes(this.currentDirection)) {
                    direction = this.currentDirection;
                    y += step;
                } else {
                    y -= step;
                }
                break;
            case "ArrowDown":
            case "Down":
            case "s":
            case "S":
                // 身体大于1 && 向反方向移动，修正为当前方向继续移动
                if (length > 1 && this._up.includes(this.currentDirection)) {
                    direction = this.currentDirection;
                    y -= step;
                } else {
                    y += step;
                }
                break;
            case "ArrowLeft":
            case "Left":
            case "a":
            case "A":
                // 身体大于1 && 向反方向移动，修正为当前方向继续移动
                if (length > 1 && this._right.includes(this.currentDirection)) {
                    direction = this.currentDirection;
                    x += step;
                } else {
                    x -= step;
                }
                break;
            case "ArrowRight":
            case "Right":
            case "d":
            case "D":
                // 身体大于1 && 向反方向移动，修正为当前方向继续移动
                if (length > 1 && this._left.includes(this.currentDirection)) {
                    direction = this.currentDirection;
                    x -= step;
                } else {
                    x += step;
                }
                break;
            default:
                // 输入其他按键按当前方向继续移动
                this.move(this.currentDirection, step, fn);
                return;

        }
        // 超出边界
        if (x < 0 || x > 290 || y < 0 || y > 290) {
            this._isLive = false;
            return;
        }
        // 碰到自己
        if (this.touchingSelf()) {
            this._isLive = false;
            return;
        }
        if (fn) {
            fn(x, y);
        }
        this.moveBody();
        this._currentDirection = direction;
        this._head.style.left = x + "px";
        this._head.style.top = y + "px";
    }

    /**
     * 增长一块
     */
    public lengthen(): void {
        this._snake.insertAdjacentHTML("beforeend", "<div></div>");
        this.moveBody(this._bodies.length - 1);
    }

    /**
     * 是否撞到自己
     * @private
     * @return  true    是
     *          false   否
     */
    private touchingSelf(): boolean {
        for (let i = 1; i < this._bodies.length; i++) {
            let body = this._bodies[i];
            if (this.x === body.offsetLeft && this.y === body.offsetTop) {
                return true;
            }
        }
        return false;
    }

    /**
     * 身体移动
     * @index       如果吃到食物，移动指定的索引位置
     * @private
     */
    private moveBody(index?: number): void {
        if (this._bodies.length > 1) {
            let last: HTMLElement;
            // 如果吃到了食物，并且当前索引移动的还不是吃到的食物
            if (index != undefined && index !== this._moveIndex) {
                // 移动吃到食物添加的
                last = this._bodies[index];
            } else {
                // 依次移动对应的
                if (this._moveIndex == undefined || this._moveIndex === 1) {
                    this._moveIndex = this._bodies.length - 1;
                } else {
                    this._moveIndex--;
                }
                last = this._bodies[this._moveIndex];
            }
            last.style.left = this.x + "px";
            last.style.top = this.y + "px";
        }
    }
}